package org.budo.support.lang.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 * @date 2016年6月4日 下午2:30:25
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Slf4j
public class ListUtil {
    /**
     * 将一个集合分割成多个集合，限制分割后每个集合的大小
     * 
     * @param src
     * @param subSize 分割后的子集合的最大长度
     */
    public static <T> List<List<T>> splitBySize(List<T> src, Integer subSize) {
        if (null == src) {
            return null;
        }

        if (null == subSize || subSize < 1) {
            throw new IllegalArgumentException("#29 subSize must > 1, subSize=" + subSize);
        }

        if (src.isEmpty()) {
            return new ArrayList<List<T>>();
        }

        List<List<T>> dest = new ArrayList<List<T>>();

        int srcSize = src.size();
        int fromIndex = 0;
        int toIndex = subSize;

        while (true) {
            if (fromIndex >= srcSize) {
                break;
            }

            List<T> subList = subList(src, fromIndex, toIndex);
            dest.add(subList);

            fromIndex += subSize;
            toIndex += subSize;
        }

        return dest;
    }

    public static <T> List<T> subList(List<T> list, Integer fromIndex) {
        if (null == list || null == fromIndex) {
            throw new IllegalArgumentException("#62 list=" + list + ", fromIndex=" + fromIndex);
        }

        return subList(list, fromIndex, list.size());
    }

    public static <T> List<T> subList(List<T> list, Integer fromIndex, Integer toIndex) {
        if (null == list || null == fromIndex || null == toIndex) {
            throw new IllegalArgumentException("#70 list=" + list + ", fromIndex=" + fromIndex + ", toIndex=" + toIndex);
        }

        if (fromIndex < 0) {
            fromIndex = 0;
        }

        int size = list.size();
        if (toIndex > size) {
            toIndex = size;
        }

        if (fromIndex == 0 && toIndex == size) {
            return list;
        }

        return list.subList(fromIndex, toIndex);
    }

    /**
     * 会跳过空的元素
     */
    public static <T> List<T> arrayToList(T[] array) {
        if (null == array) {
            return null;
        }

        List<T> list = new ArrayList<T>();
        for (T each : array) {
            if (null != each) {
                list.add(each);
            }
        }
        return list;
    }

    public static <T> List<T> arrayList(T... array) {
        if (null == array) {
            return null;
        }

        List<T> list = new ArrayList<T>();
        for (T each : array) {
            if (null != each) {
                list.add(each);
            }
        }

        return list;
    }

    /**
     * 会跳过空的元素
     */
    public static <T> List<T> toList(T... array) {
        return arrayList(array);
    }

    /**
     * 注意传入的数据得是数组
     */
    public static <T> List<String> toStringList(T... array) {
        if (null == array) {
            return null;
        }

        List<String> list = new ArrayList<String>();
        for (T each : array) {
            if (null == each) {
                list.add(null);
            } else {
                list.add(each.toString());
            }
        }

        return list;
    }

    public static <T> List<T> append(List<T> list, T item) {
        if (null == list) {
            list = new ArrayList<T>();
        }

        if (null == item) {
            return list;
        }

        list.add(item);
        return list;
    }

    public static <T> List<T> concat(List<T> first, List<?>... lists) {
        List<T> list = new ArrayList<T>();
        if (null != first) {
            list.addAll(first);
        }

        if (null == lists || lists.length < 1) { // 后面没有了
            return list;
        }

        for (List<?> each : lists) {
            addAll(list, each);
        }
        return list;
    }

    public static Boolean isNullOrEmpty(List<?> list) {
        return null == list || list.isEmpty();
    }

    /**
     * 判断空值并防止越界
     */
    public static <T> T getItem(List<T> list, Integer index) {
        return getItem(list, index, null);
    }

    public static <T> T getItem(List<T> list, Integer index, T defaultValue) {
        boolean _default = null == list || null == index || index < 0 || index >= list.size();

        return _default ? defaultValue : list.get(index);
    }

    public static Integer size(Collection<?> list) {
        return null == list ? null : list.size();
    }

    public static <T> List<T> newList(Collection<T> collection) {
        if (null == collection) {
            return null;
        }
        return new ArrayList<T>(collection);
    }

    public static <T> T first(List<T> list) {
        return getItem(list, 0);
    }

    public static <T> T last(List<T> list) {
        return (null == list || list.isEmpty()) ? null : list.get(list.size() - 1);
    }

    public static <T> List<T> remove(List<T> list, T... remove) {
        List<T> result = new ArrayList<T>();
        for (T each : list) {
            if (!ArrayUtil.contains(remove, each)) {
                result.add(each);
            }
        }
        return result;
    }

    public static <T> List<T> enumerationToList(Enumeration<T> enumeration) {
        if (null == enumeration) {
            return null;
        }

        List<T> list = new ArrayList<T>();
        while (enumeration.hasMoreElements()) {
            T each = enumeration.nextElement();
            list.add(each);
        }
        return list;
    }

    public static <T> List<T> iteratorToList(Iterator<T> iterator) {
        if (null == iterator) {
            return null;
        }

        List<T> list = new ArrayList<T>();
        while (iterator.hasNext()) {
            T each = iterator.next();
            list.add(each);
        }
        return list;
    }

    public static <T> void add(List<T> to, T one) {
        if (null == to) {
            log.warn("#232 to=" + to + ", one=" + one);
            return;
        }

        if (null == one) {
            log.warn("#237 to=" + to + ", one=" + one);
            return;
        }

        to.add(one);
    }

    /**
     * @return
     * @see #addAllSkipNull(List, List)
     */
    public static <T> Collection<T> addAll(Collection<T> to, Collection<?> from) {
        if (null == to) {
            log.warn("#192 addAll, to=" + to + ", from=" + from);
            return to;
        }

        if (null == from) {
            log.warn("#235 addAll, to=" + to + ", from=" + from);
            return to;
        }

        to.addAll((Collection<T>) from);
        return to;
    }

    /**
     * 将传入的list 复制一份 倒序后返回
     */
    public static <T> List<T> reverse(List<T> list) {
        if (null == list || list.isEmpty()) {
            return list;
        }

        List<T> newList = new ArrayList<T>(list);
        Collections.reverse(newList);
        return newList;
    }

    public static <T> List<List<T>> partition(List<T> list, Integer size) {
        if (null == list || null == size //
                || size < 1 //
                || list.size() <= size) {
            return toList(list);
        }

        List<List<T>> result = new ArrayList<List<T>>();
        for (int i = 0; i < list.size(); i = i + size) {
            int toIndex = i + size;
            List<T> subList = subList(list, i, toIndex);
            result.add(subList);
        }
        return result;
    }

    public static <T> List<T> duplicate(T each, Integer count) {
        List<T> list = new ArrayList<T>();
        for (int i = 0; i < count; i++) {
            list.add(each);
        }
        return list;
    }

    public static <T> List<T> shuffle(List<T> list) {
        if (null == list || list.size() < 2) {
            return list;
        }

        Collections.shuffle(list);
        return list;
    }

    public static List<String> toStringList_2(List<Object> list) {
        if (null == list) {
            return null;
        }

        List<String> strs = new ArrayList<String>();
        for (Object each : list) {
            strs.add(StringUtil.toString(each));
        }
        return strs;
    }

    public static <T> List<T> subList_0(List<T> list, Integer fromIndex, Integer toIndex) {
        if (null == list || null == fromIndex || null == toIndex) {
            throw new IllegalArgumentException("#70 list=" + list + ", fromIndex=" + fromIndex + ", toIndex=" + toIndex);
        }

        if (toIndex > list.size()) {
            throw new IndexOutOfBoundsException("#302 toIndex = " + toIndex + ", size=" + list.size());
        }

        return list.subList(fromIndex, toIndex);
    }

    /**
     * 返回增加的数据
     */
    public static <T> List<T> addition(List<T> _old, List<T> _new) {
        if (null == _new || _new.isEmpty()) {
            log.error("#355 addition, _old=" + _old + ", _new=" + _new);
            return _new;
        }

        List<T> list = new ArrayList<T>();

        for (T each : _new) {
            if (!_old.contains(each)) {
                list.add(each);
            }
        }

        return list;
    }

    public static <T> List<T> subtraction(List<T> _old, List<T> _new) {
        if (null == _old || _old.isEmpty()) {
            log.error("#371 subtraction, _old=" + _old + ", _new=" + _new);
            return _old;
        }

        List<T> list = new ArrayList<T>();

        for (T each : _old) {
            if (null == _new || !_new.contains(each)) {
                list.add(each);
            }
        }

        return list;
    }

    public static <T> List<T> addNoRepeat(List<T> _old, List<T> _new) {
        for (T _each : _new) {
            if (_old.contains(_each)) {
                continue;
            }

            _old.add(_each);
        }

        return _old;
    }

    public static <T> List<T> additionNoRepeat(List<T> _old, List<T> _new) {
        List<T> addition = addition(_old, _new);
        return noRepeat(addition);
    }

    public static <T> List<T> subtractionNoRepeat(List<T> _old, List<T> _new) {
        List<T> subtraction = subtraction(_old, _new);
        return noRepeat(subtraction);
    }

    private static <T> List<T> noRepeat(List<T> in) {
        if (null == in || in.isEmpty()) {
            return in;
        }

        List<T> out = new ArrayList<T>();
        for (T each : in) {
            if (!out.contains(each)) {
                out.add(each);
            }
        }

        return out;
    }

    public static Boolean contains(List<?> list, Object item) {
        if (null == list || null == item || list.isEmpty()) {
            return false;
        }

        return list.contains(item);
    }

    public static Boolean containsNumber(List<?> list, Number item) {
        if (null == list || null == item || list.isEmpty()) {
            return false;
        }

        if (list.contains(item)) {
            return true;
        }

        for (Object each : list) {
            if (NumberUtil.equals(each, item)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @see #addAll(Collection, Collection)
     */
    public static <T> void addAllSkipNull(Collection<T> to, Collection<T> from) {
        if (null == to) {
            log.warn("#192 to=" + to + ", from=" + from);
            return;
        }

        if (null == from) {
            log.warn("#235 to=" + to + ", from=" + from);
            return;
        }

        for (T each : from) {
            if (null == each) {
                continue;
            }

            to.add(each);
        }
    }

    public static Integer findIndex(List<?> list, Object one) {
        if (null == list || null == one) {
            log.error("#443 list.size=" + ListUtil.size(list) + ", one=" + one);
            return null;
        }

        for (int i = 0; i < list.size(); i++) {
            Object each = list.get(i);
            if (one.equals(each)) {
                return i;
            }
        }

        log.debug("#454 list.size={}, one={}", size(list), one);
        return null;
    }

    public static Integer findIndexByString(List<?> list, Object one) {
        if (null == list || null == one) {
            log.error("#427 list.size=" + ListUtil.size(list) + ", one=" + one);
            return null;
        }

        for (int i = 0; i < list.size(); i++) {
            Object each = list.get(i);
            if (StringUtil.equals(each, one)) {
                return i;
            }
        }

        log.error("#438 list.size=" + ListUtil.size(list) + ", one=" + one);
        return null;
    }

    public static <T> List<T> sort(Collection<T> in) {
        if (null == in) {
            return null;
        }

        if (in instanceof List && in.isEmpty()) {
            return (List<T>) in;
        }

        if (in.isEmpty()) {
            return Collections.EMPTY_LIST;
        }

        if (in instanceof List) {
            Collections.sort(((List) (Object) in));
            return (List<T>) in;
        }

        List out = new ArrayList(in);

        Collections.sort(out, new Comparator() {
            public int compare(Object o1, Object o2) {
                if (o1 == o2) { // 都空 或相等
                    return 0;
                }

                if (null == o1) {
                    return -1;
                }

                if (null == o2) {
                    return 1;
                }

                if (o1 instanceof Comparable && o2 instanceof Comparable) {
                    return ((Comparable) o1).compareTo(o2);
                }

                return o1.toString().compareTo(o2.toString());
            }
        });
        return out;
    }

    public static boolean isNotEmpty(List<?> list) {
        return !isNullOrEmpty(list);
    }

    public static Boolean equals(List<?> a, List<?> b) {
        if ((null == a || a.isEmpty()) && (null == b || b.isEmpty())) {
            return true; // 都为空
        }

        if ((null == a || a.isEmpty()) || (null == b || b.isEmpty())) {
            return false; // 一个为空
        }

        // 直接等
        if (a.equals(b)) {
            return true;
        }

        // 不一样大
        if (!NumberUtil.equals(a.size(), b.size())) {
            return false;
        }

        for (int i = 0; i < a.size(); i++) {
            Object aItem = a.get(i);
            Object bItem = b.get(i);

            if (null == aItem && null == bItem) {
                continue; // 俩都为空 等 比下一个
            }

            if (null == aItem || null == bItem) {
                return false; // 其中一个为空 不等 直接返回
            }

            if (!aItem.equals(bItem)) { // 不等
                return false;
            }
        }

        return true;
    }

    public static String toStringSimply(List<?> list) {
        if (null == list || list.size() < 10) {
            return list + "";
        }

        return list.subList(0, 5) + " ... " + list.subList(list.size() - 5, list.size());
    }

    public static <T> Map<String, List<T>> groupByField(List<T> list, Function<T, String> function) {
        Map<String, List<T>> map = new HashMap<String, List<T>>();

        for (T each : list) {
            String group = function.apply(each);
            List<T> groupList = map.get(group);
            if (null == groupList) {
                groupList = new ArrayList<T>();
                map.put(group, groupList);
            }

            groupList.add(each);
        }

        return map;
    }

    /**
     * 排序
     * 
     * @param function 返回每个元素的顺序号
     */
    public static <T> List<T> sort(List<T> input, Function<T, Object> function) {
        if (null == input || null == function) {
            return input;
        }

        List out = new ArrayList(input);

        Collections.sort(out, new Comparator() {
            public int compare(Object o1, Object o2) {
                Object a1 = function.apply((T) o1);
                Object a2 = function.apply((T) o2);

                if (a1 == a2) { // 都空 或相等
                    return 0;
                }

                if (null == a1) {
                    return -1;
                }

                if (null == a2) {
                    return 1;
                }

                if (a1 instanceof Comparable && a2 instanceof Comparable) {
                    return ((Comparable) a1).compareTo(a2);
                }

                return a1.toString().compareTo(a2.toString());
            }
        });

        return out;
    }

    /**
     * 排重
     */
    public static <T> List<T> distinct(List<T> input, Function<T, Object> function) {
        if (null == input || null == function) {
            return input;
        }

        Set<Object> set = new HashSet<Object>();
        List<T> out = new ArrayList<T>();

        for (T each : input) {
            if (null == each) {
                out.add(each);
                continue;
            }

            Object key = function.apply(each);
            if (null == key) {
                out.add(each);
                continue;
            }

            if (set.contains(key)) {
                continue; // 重复了
            }

            set.add(key); // 记一下
            out.add(each);
        }

        return out;
    }
}